package com.spa.application.service.shop;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.spa.application.command.shop.MassagistAddCommand;
import com.spa.application.command.shop.MassagistDelCommand;
import com.spa.application.command.shop.MassagistEditCommand;
import com.spa.application.dto.PageResult;
import com.spa.application.dto.shop.ActualMassagistInfoDTO;
import com.spa.application.dto.shop.IndexDTO;
import com.spa.application.dto.shop.MassagistBaseInfoDTO;
import com.spa.application.dto.shop.OrderListDTO;
import com.spa.application.dto.shop.ShopOrderAllDTO;
import com.spa.application.query.PageQuery;
import com.spa.application.query.shop.IndexDataQuery;
import com.spa.application.query.shop.MassagistListQuery;
import com.spa.application.query.shop.OrderListQuery;
import com.spa.application.query.shop.OrderQuery;
import com.spa.domain.entity.MassageServiceEntity;
import com.spa.domain.entity.Massagist;
import com.spa.domain.entity.OrderInfo;
import com.spa.domain.entity.Shop;
import com.spa.domain.service.AddressService;
import com.spa.domain.service.MassageService;
import com.spa.domain.service.MassagistService;
import com.spa.domain.service.OrderService;
import com.spa.domain.service.OrderTransferService;
import com.spa.domain.service.RuleService;
import com.spa.domain.service.ShopService;
import com.spa.domain.service.TicketService;
import com.spa.infrastructure.common.CommonCode;
import com.spa.infrastructure.common.ResponseHelper;
import com.spa.infrastructure.common.ResponseMessage;
import com.spa.infrastructure.enums.dict.massagist.MassagistStatusEnum;
import com.spa.infrastructure.enums.dict.massagist.MassagistTypeEnum;
import com.spa.infrastructure.enums.dict.order.OrderOperStatusEnum;
import com.spa.infrastructure.enums.dict.order.OrderStatusEnum;
import com.spa.infrastructure.exception.SpaException;
import com.spa.infrastructure.mapper.MassagistMapperStruct;
import com.spa.infrastructure.mapper.OrderMapperStruct;
import com.spa.infrastructure.repository.condition.MassagistCondition;
import com.spa.infrastructure.repository.condition.OrderInfoCondition;
import com.spa.infrastructure.util.DateUtil;
import com.spa.infrastructure.util.OrderUtils;
import com.spa.infrastructure.util.RedisUtil;
import com.spa.infrastructure.util.SessionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 门店首页相关
 */
@Service
@Slf4j
public class ShopIndexService {


    @Autowired
    private MassagistService massagistService;

    @Autowired
    private ShopService shopService;

    @Autowired
    public ResponseHelper responseHelper;

    @Autowired
    private OrderService orderService;

    @Autowired
    private MassageService massageService;

    @Autowired
    private AddressService addressService;

    @Autowired
    private TicketService ticketService;

    @Resource
    private RedisUtil redisUtil;

    @Autowired
    private OrderTransferService orderTransferService;

    @Resource
    private RuleService ruleService;

    public IndexDTO index(@RequestBody IndexDataQuery query) {
        Long shopId = SessionUtil.getUser().getShopId();
        Shop shop = shopService.getShopById(shopId);
        LocalDate currentMonth = LocalDate.now().plusMonths(query.getIndex());
        LocalDate lastMonth = currentMonth.minusMonths(1);
        String currentMonthFirstDay =
                DateUtil.date2Str(DateUtil.getStartTimeOfCurrentMonth(DateUtil.localDate2Date(currentMonth)));
        String currentMonthLastDay =
                DateUtil.date2Str(DateUtil.getEndTimeOfCurrentMonth(DateUtil.localDate2Date(currentMonth)));
        String lastMonthFirstDay =
                DateUtil.date2Str(DateUtil.getStartTimeOfCurrentMonth(DateUtil.localDate2Date(lastMonth)));
        String lastMonthLastDay =
                DateUtil.date2Str(DateUtil.getEndTimeOfCurrentMonth(DateUtil.localDate2Date(lastMonth)));
        //通过门店用户ID查询门店信息
        IndexDTO indexDTO = IndexDTO.builder().build();
        indexDTO.setName(shop.getName());
        IndexDTO.StatisticsDataDTO lastMonthStatisticsData = getStatisticsDataByMonth(lastMonthFirstDay,
                lastMonthLastDay, shopId);
        IndexDTO.StatisticsDataDTO currentMonthStatisticsData = getStatisticsDataByMonth(currentMonthFirstDay,
                currentMonthLastDay, shopId);
        currentMonthStatisticsData.setBuyUsersComparison(calculateTheQuarterRatio(lastMonthStatisticsData.getBuyUsers(), currentMonthStatisticsData.getBuyUsers()));
        currentMonthStatisticsData.setTotalPriceComparison(calculateTheQuarterRatio(lastMonthStatisticsData.getTotalPrice(), currentMonthStatisticsData.getTotalPrice()));
        indexDTO.setCurrentMonth(currentMonthStatisticsData);
        return indexDTO;
    }

    /**
     * 计算环比数据
     */
    private static double calculateTheQuarterRatio(double last, double current) {
        if (last == 0 && current == 0) {
            return 0;
        }
        if (last == 0) {
            return 100;
        }
        if (current > last) {
            return Math.round(((current - last) / last) * 100);
        }
        return -Math.round(((last - current) / last) * 100);
    }

    /**
     * 按月统计数据
     *
     * @param begin 开始时间
     * @param end   结束时间
     * @return 统计数据
     */
    private IndexDTO.StatisticsDataDTO getStatisticsDataByMonth(String begin, String end, Long shopId) {
        IndexDTO.StatisticsDataDTO statisticsDataDTO = IndexDTO.StatisticsDataDTO.builder().build();
        List<IndexDTO.Pair> buyUsersList = new ArrayList<>();
        List<IndexDTO.Pair> totalPriceList = new ArrayList<>();
        List<OrderInfo> orderInfos = shopService.getOverOrderList(shopId, begin, end);
        List<OrderInfo> inServiceOrders = new ArrayList<>(orderInfos.stream()
                .filter(orderInfo -> Objects.equals(orderInfo.getStatus(), OrderStatusEnum.$40.getCode()))
                .toList());
        //已取消订单（技师已出发或已到达）
        List<OrderInfo> cancel = orderInfos.stream().filter(orderInfo -> Objects.equals(orderInfo.getStatus(), OrderStatusEnum.$99.getCode())
                && (Objects.equals(orderInfo.getOrderOperStatus(), OrderOperStatusEnum.$20.getCode())
                || Objects.equals(orderInfo.getOrderOperStatus(), OrderOperStatusEnum.$30.getCode()))).toList();
        if (CollectionUtil.isNotEmpty(cancel)) {
            inServiceOrders.addAll(cancel);
        }
        inServiceOrders.forEach(orderInfo -> {
            Date time = ObjectUtil.isNotNull(orderInfo.getEndTime()) ? orderInfo.getEndTime() : orderInfo.getCancelTime();
            LocalDate localDate = OrderUtils.toLocalDateTime(time).toLocalDate();
            orderInfo.setDate(localDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
        });
        Map<String, List<OrderInfo>> map = inServiceOrders.stream().collect(Collectors.groupingBy(OrderInfo::getDate));
        if (CollUtil.isNotEmpty(map)) {
            map.forEach(((s, order) -> {
                List<OrderInfo> complete = order.stream()
                        .filter(orderInfo -> Objects.equals(orderInfo.getStatus(), OrderStatusEnum.$40.getCode()))
                        .toList();
                List<OrderInfo> cancel0 = order.stream()
                        .filter(orderInfo -> Objects.equals(orderInfo.getStatus(), OrderStatusEnum.$99.getCode()))
                        .toList();
                IndexDTO.Pair buyUser = IndexDTO.Pair.builder()
                        .key(s)
                        .value(String.valueOf(complete.size()))
                        .build();
                buyUsersList.add(buyUser);
                int sum = complete.stream().mapToInt(OrderInfo::getOrderPrice).sum();
                int ext = complete.stream().mapToInt(OrderInfo::getExtTotalPrice).sum();
                int cancelPrice = cancel0.stream().mapToInt(OrderInfo::getTravelPrice).sum();
                IndexDTO.Pair totalPrice = IndexDTO.Pair.builder()
                        .key(s)
                        .value(BigDecimal.valueOf(sum + ext + cancelPrice).divide(BigDecimal.valueOf(100), 2, RoundingMode.FLOOR).toString())
                        .build();
                totalPriceList.add(totalPrice);
            }));
        }
        buyUsersList.sort(Comparator.comparingInt(o -> o.getKey().hashCode()));
        totalPriceList.sort(Comparator.comparingInt(o -> o.getKey().hashCode()));
        statisticsDataDTO.setBuyUsersList(buyUsersList);
        List<OrderInfo> complete = inServiceOrders.stream()
                .filter(orderInfo -> Objects.equals(orderInfo.getStatus(), OrderStatusEnum.$40.getCode()))
                .toList();
        statisticsDataDTO.setBuyUsers(complete.size());
        int sum = complete.stream().mapToInt(OrderInfo::getOrderPrice).sum();
        int ext = complete.stream().mapToInt(OrderInfo::getExtTotalPrice).sum();
        int cancelPrice = cancel.stream().mapToInt(OrderInfo::getTravelPrice).sum();
        statisticsDataDTO.setTotalPrice(sum + ext + cancelPrice);
        statisticsDataDTO.setTotalPriceList(totalPriceList);
        return statisticsDataDTO;
    }

    public PageResult<MassagistBaseInfoDTO> queryMassagistList(@RequestBody MassagistListQuery query) {
        MassagistCondition massagistCondition = new MassagistCondition();
        massagistCondition.setStatus(query.getStatus());
        massagistCondition.setCurrent(query.getCurrent());
        massagistCondition.setSize(query.getSize());
        massagistCondition.setShopId(SessionUtil.getUser().getShopId());
        Page<Massagist> massagistPage = massagistService.page(massagistCondition);
        List<Massagist> massagistList = massagistPage.getRecords();
        Map<Long, List<Massagist>> map = massagistList.stream().collect(Collectors.groupingBy(Massagist::getId));
        PageResult<MassagistBaseInfoDTO> massagistBaseInfoDTOPageResult = new PageResult<>();
        List<MassagistBaseInfoDTO> massagistBaseInfoDTOS = new ArrayList<>();
        massagistList.forEach(massagist -> {
            MassagistBaseInfoDTO massagistBaseInfoDTO = MassagistMapperStruct.INSTANCE.entity2Dto(massagist);
            List<MassageServiceEntity> massageServiceEntities =
                    map.get(massagist.getId()).stream().findFirst().get().getMassageServiceEntities();
            if (ObjectUtil.isNotNull(massageServiceEntities)) {
                List<String> serviceNames = massageServiceEntities.stream().map(MassageServiceEntity::getName).toList();
                massagistBaseInfoDTO.setServiceNames(serviceNames);
            }
            massagistBaseInfoDTOS.add(massagistBaseInfoDTO);
        });
        massagistBaseInfoDTOPageResult.setTotal(massagistPage.getTotal());
        massagistBaseInfoDTOPageResult.setCurrent(massagistPage.getCurrent());
        massagistBaseInfoDTOPageResult.setSize(massagistPage.getSize());
        massagistBaseInfoDTOPageResult.setRecords(massagistBaseInfoDTOS);
        return massagistBaseInfoDTOPageResult;
    }

    /**
     * 新增技师
     */
    @Transactional
    public ResponseMessage addMassagist(@Validated @RequestBody MassagistAddCommand command) {
        String lockKey = "shop：addMassagist：lock:" + command.getPhone();
        String id = UUID.fastUUID().toString(true);
        try {
            boolean lockFlag = redisUtil.getLock(lockKey, id);
            if (!lockFlag) {
                throw new SpaException("操作频繁！");
            }
            if (massagistService.isExistByPhone(command.getPhone())) {
                return responseHelper.fail(CommonCode.MASSAGIST_ALREADY_EXIST);
            }
            command.setShopId(SessionUtil.getUser().getShopId());
            Long massagistId = massagistService.addMassagistInfo(command);

            ruleService.addMassagistRule(command.getRuleParamList(), command.getShopId(), massagistId);

        } finally {
            redisUtil.releaseLock(lockKey, id);
        }
        return responseHelper.success();
    }

    @Transactional
    public void editMassagist(MassagistEditCommand command) {
        Massagist massagist = massagistService.getInfoByMassagistId(command.getMassagistId());
        massagistService.editMassagistInfo(command);
        ruleService.addMassagistRule(command.getRuleParamList(), massagist.getShopId(), massagist.getId());
    }


    public void delMassagist(@RequestBody MassagistDelCommand command) {
        massagistService.delMassagist(command);
    }

    public PageResult<OrderListDTO> queryOrderList(@RequestBody OrderListQuery query) throws ParseException {
        Long shopId = SessionUtil.getUser().getShopId();
        Date date = DateUtil.str2Date(query.getBeginDate(), "yyyy-MM-dd");
        Date endDate = DateUtil.str2Date(query.getEndDate(), "yyyy-MM-dd");
        query.setBeginDate(DateUtil.date2Str(DateUtil.getStartTimeOfCurrentDay(date)));
        query.setEndDate(DateUtil.date2Str(DateUtil.getEndTimeOfCurrentDay(endDate)));
        OrderInfoCondition orderCondition = OrderMapperStruct.INSTANCE.query2Condition(query, shopId);
        //后端兼容性处理 后续看是否能进行优化
        if (orderCondition.getStatus() == 0) {
            orderCondition.setStatus(null);
        }
        OrderService.OrderInfoPlug plug = OrderService.OrderInfoPlug.base();
        plug.setTicket(true);
        plug.setEvaluate(true);
        Page<OrderInfo> orderInfoPage = orderService.pageByCondition(orderCondition, plug);
        PageResult<OrderListDTO> orderListDTOPageResult =
                OrderMapperStruct.INSTANCE.entity2OrderListDTO4Page(orderInfoPage);
        orderListDTOPageResult.getRecords().forEach(dto -> {
            if (orderTransferService.existsWaitByOrderId(Long.parseLong(dto.getId()), null)) {
                dto.setTransfer(true);
            }
        });
        return orderListDTOPageResult;
    }

    public ShopOrderAllDTO queryOrderDetail(@RequestBody OrderQuery query) {
        OrderService.OrderInfoPlug plug = OrderService.OrderInfoPlug.base();
        plug.setTicket(true);
        plug.setEvaluate(true);
        OrderInfo orderInfo = orderService.getOrderInfoById(query.getOrderId(), plug);
        ShopOrderAllDTO shopOrderAllDTO = OrderMapperStruct.INSTANCE.entity2ShopDto(orderInfo);
        return shopOrderAllDTO;
    }

    public PageResult<ActualMassagistInfoDTO> queryActualMassagistList(PageQuery query) {
        PageResult<ActualMassagistInfoDTO> massagistDTOPageResult = new PageResult<>();
        MassagistCondition massagistCondition = new MassagistCondition();
        massagistCondition.setCurrent(query.getCurrent());
        massagistCondition.setSize(query.getSize());
        massagistCondition.setShopId(SessionUtil.getUser().getShopId());
        massagistCondition.setTag(MassagistTypeEnum.$10.getCode());
        massagistCondition.setInStatus(Lists.newArrayList(MassagistStatusEnum.$20.getCode(), MassagistStatusEnum.$30.getCode()));
        Page<Massagist> massagistPage = massagistService.page(massagistCondition);

        massagistDTOPageResult.setTotal(massagistPage.getTotal());
        massagistDTOPageResult.setCurrent(massagistPage.getCurrent());
        massagistDTOPageResult.setSize(massagistPage.getSize());
        massagistDTOPageResult.setRecords(massagistPage.getRecords().stream().map(MassagistMapperStruct.INSTANCE::entity2actualDto).toList());

        return massagistDTOPageResult;
    }


}
